#include "hikari.h"
#include "ObjectCreators.h"



///MATERIAL
void setTextures(HObj*object, FileSection*texturesSection,HikariContext*context);
void setColors(HObj*object, FileSection*colorsSection,HikariContext*context);
void setFlags(HObj*object, FileSection*flagSection);
HObj* MaterialLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	object->getInternalNode()->setVisible(section->readBoolean(section->getChunk(L"vis")));
	vector<Chunk*>subSectionChunks=section->getAllSections();
	for(unsigned int i=0;i<subSectionChunks.size();i++){
		Chunk*subSection=subSectionChunks[i];
		if(Utils::equal(subSection->getName(),L"flags")){
			FileSection*flagSection=section->readSection(subSection);
			setFlags(object, flagSection);
			delete flagSection;
			flagSection=NULL;
		}else if(Utils::equal(subSection->getName(),L"textures")){
			FileSection*texturesSection=section->readSection(subSection);
			setTextures(object, texturesSection, context);
			delete texturesSection;
			texturesSection=NULL;
		}else if(Utils::equal(subSection->getName(),L"colors")){
			FileSection*colorsSection=section->readSection(subSection);
			setColors(object, colorsSection, context);
			delete colorsSection;
			colorsSection=NULL;
		}
	}
	return object;
}

void setTextures(HObj*object, FileSection*texturesSection,HikariContext*context){
	vector<Chunk*>texturesChunks=texturesSection->getAllAttributes();
	int textureIndex = 0;
	for(unsigned int i=0;i<texturesChunks.size();i++){
		Chunk*chunk=texturesChunks[i];
		if(Utils::equal(chunk->getName(),L"matType")){
			video::E_MATERIAL_TYPE materialType=(video::E_MATERIAL_TYPE)texturesSection->readLong(chunk);
			object->getInternalNode()->getMaterial(0).MaterialType=materialType;
		}else if(Utils::equal(chunk->getName(),L"path")){
			wstring path=texturesSection->readString(chunk);
			wstring mediaFolder=context->getMediaFolder();
			video::ITexture*texture=context->getDriver()->getTexture(irr::io::path(Utils::wstring_to_string(mediaFolder+path).c_str()));
			object->getInternalNode()->getMaterial(0).setTexture(textureIndex, texture);
			textureIndex++;
		}
	}
}

void setColors(HObj*object, FileSection*colorsSection,HikariContext*context){
	vector<Chunk*>colorsChunks=colorsSection->getAllAttributes();
	video::SMaterial material=object->getInternalNode()->getMaterial(0);
	for(unsigned int i=0;i<colorsChunks.size();i++){
		Chunk*chunk=colorsChunks[i];
		if(Utils::equal(chunk->getName(),L"a")){
			video::SColor color=colorsSection->readSColor(chunk);
			material.AmbientColor=color;
		}else if(Utils::equal(chunk->getName(),L"d")){
			video::SColor color=colorsSection->readSColor(chunk);
			material.DiffuseColor=color;
		}else if(Utils::equal(chunk->getName(),L"e")){
			video::SColor color=colorsSection->readSColor(chunk);
			material.EmissiveColor=color;
		}else if(Utils::equal(chunk->getName(),L"spec")){
			video::SColor color=colorsSection->readSColor(chunk);
			material.SpecularColor=color;
		}else if(Utils::equal(chunk->getName(),L"shin")){
			float shiness=colorsSection->readFloat(chunk);
			material.Shininess=shiness;
		}
	}
}

void setFlags(HObj*object, FileSection*flagSection){
	vector<Chunk*>flagsChunks=flagSection->getAllAttributes();
	for(unsigned int i=0;i<flagsChunks.size();i++){
		Chunk*flag=flagsChunks[i];
		if(Utils::equal(flag->getName(),L"flag")){
			vector<long long>*flagValues=flagSection->readLongs(flag);
			int flag=(int)(*flagValues)[0];
			bool value=(*flagValues)[1]!=0;
			object->getInternalNode()->setMaterialFlag((video::E_MATERIAL_FLAG)flag, value);
			delete flagValues;
			flagValues=NULL;
		}
	}
}

///TRANSFORMATION
vector3df readVector(FileSection*section);
HObj* TransformationLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	wstring name=section->readString(section->getChunk(L"name"));
	object->setName(name);
	vector<Chunk*>subSectionChunks=section->getAllSections();
	for(unsigned int i=0;i<subSectionChunks.size();i++){
		Chunk*subSection=subSectionChunks[i];
		if(Utils::equal(subSection->getName(),L"pos")){
			FileSection*posSection=section->readSection(subSection);
			vector3df pos=readVector(posSection);
			object->setPosition(pos);
			delete posSection;
			posSection=NULL;
		}else if(Utils::equal(subSection->getName(),L"rot")){
			FileSection*rotSection=section->readSection(subSection);
			vector3df rot=readVector(rotSection);
			object->setRotation(rot);
			delete rotSection;
			rotSection=NULL;
		}else if(Utils::equal(subSection->getName(),L"scl")){
			FileSection*scaleSection=section->readSection(subSection);
			vector3df scale=readVector(scaleSection);
			object->setScale(scale);
			delete scaleSection;
			scaleSection=NULL;
		}
	}
	return object;;
}

vector3df readVector(FileSection*section){
	float x=section->readFloat(section->getChunk(L"x"));
	float y=section->readFloat(section->getChunk(L"y"));
	float z=section->readFloat(section->getChunk(L"z"));
	return vector3df(x,y,z);
}

///CUBE
HObj* CubeLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ISceneNode*cubeNode=context->getSceneManager()->addCubeSceneNode(3.f);
	HObj*nobject=new HObj(cubeNode);
	return nobject;
}

HObj* SphereLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ISceneNode*sphereNode=context->getSceneManager()->addSphereSceneNode(3.f);
	HObj*nobject=new HObj(sphereNode);
	return nobject;
}

HObj* CustomModelLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	wstring path(section->readString(section->getChunk(L"path")));
	path=context->getMediaFolder()+path;
	IAnimatedMesh*animatedMesh=context->getSceneManager()->getMesh(io::path(path.c_str()));
	IAnimatedMeshSceneNode*animatedMeshSceneNode=context->getSceneManager()->addAnimatedMeshSceneNode(animatedMesh);
	IAnimatedMeshSceneNode*tnode1=dynamic_cast<IAnimatedMeshSceneNode*>(animatedMeshSceneNode);
	HObj*nobject=new HObj(animatedMeshSceneNode);
	ISceneNode*tn3=nobject->getInternalNode();
	IAnimatedMeshSceneNode*tnode=dynamic_cast<IAnimatedMeshSceneNode*>(tn3);
	AnimationData*animData=context->getAnimationManager()->getAnimationData(path);
	nobject->setAnimationData(animData);
	return nobject;
}

HObj* AnimationLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ISceneNode*node=object->getInternalNode();
	IAnimatedMeshSceneNode*animNode=dynamic_cast<IAnimatedMeshSceneNode*>(node);
	if(animNode!=NULL){
		bool immidiatelyAnimated=section->readBoolean(section->getChunk(L"immAnim"));
		bool loop=section->readBoolean(section->getChunk(L"lp"));
		animNode->setLoopMode(loop);
		animNode->setFrameLoop(0,0);
		if(immidiatelyAnimated){
			wstring selectedTrack=section->readString(section->getChunk(L"selTrk"));
			if(!Utils::equal(selectedTrack,L"")){
				object->animate(selectedTrack);
			}
		}
	}
	return object;
}

HObj* CameraInstantiateLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ICameraSceneNode*camera=context->getSceneManager()->addCameraSceneNode();
	ISceneNode*target=context->getSceneManager()->addEmptySceneNode(camera);
	target->setPosition(vector3df(0,0,1));
	HObjCamera*nobject=new HObjCamera(camera);
	nobject->setTarget(target);
	context->getCameras().push_back(nobject);
	return nobject;
}

HObj* TargetCameraInstantiateLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ICameraSceneNode*camera=context->getSceneManager()->addCameraSceneNode();
	HObjCamera*nobject=new HObjCamera(camera);
	nobject->setTarget(NULL);
	return nobject;
}

HObj* CameraLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ICameraSceneNode*camera=(ICameraSceneNode*)object->getInternalNode();
	float fov = section->readFloat(section->getChunk(L"fov"));
    
	float nearv =    
		section->readFloat(section->getChunk(L"near"));
    float farv = section->readFloat(section->getChunk(L"far"));
    float aspect = section->readFloat(section->getChunk(L"aspect"));
	camera->setFOV(fov);
	camera->setNearValue(nearv);
	camera->setFarValue(farv);
	camera->setAspectRatio(aspect);
	return object;
}

HObj* ConeLightLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ILightSceneNode*light=context->getSceneManager()->addLightSceneNode();
	light->setLightType(video::ELT_SPOT);
	HObj*nobject=new HObj(light);
	return nobject;
}

HObj* OmniLightLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ILightSceneNode*light=context->getSceneManager()->addLightSceneNode();
	light->setLightType(video::ELT_POINT);
	HObj*nobject=new HObj(light);
	return nobject;
}

HObj* DirectionalLightLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ILightSceneNode*light=context->getSceneManager()->addLightSceneNode();
	light->setLightType(video::ELT_DIRECTIONAL);
	SLight&data=light->getLightData();
	data.Type=video::ELT_DIRECTIONAL;
	light->setLightData(data);

	HObj*nobject=new HObj(light);
	return nobject;
}

HObj* LightLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ILightSceneNode*light=(ILightSceneNode*)object->getInternalNode();
	video::SLight&data=light->getLightData();
	float radius=section->readFloat(section->getChunk(L"rad"));
	float outer = section->readFloat(section->getChunk(L"outerCone"));
    float inner = section->readFloat(section->getChunk(L"innerCone"));
    float falloff = section->readFloat(section->getChunk(L"falloff"));
	data.Radius=radius;
	data.OuterCone=outer;
	data.InnerCone=inner;
	data.Falloff=falloff;
	video::SColor ambient=section->readSColor(section->getChunk(L"amb"));
	video::SColor specular=section->readSColor(section->getChunk(L"spec"));
	video::SColor diffuse=section->readSColor(section->getChunk(L"diff"));
	data.AmbientColor=ambient;
	data.SpecularColor=specular;
	data.DiffuseColor=diffuse;
	light->setLightData(data);
	return object;
}

HObj* BillBoardLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	IBillboardSceneNode*billboard=context->getSceneManager()->addBillboardSceneNode();
	HObj*nobject=new HObj(billboard);
	return nobject;
}

HObj* ParticleInstantiateLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	IParticleSystemSceneNode*particles=(IParticleSystemSceneNode*)context->getSceneManager()->addParticleSystemSceneNode(false);
	HObj*nobject=new HObj(particles);
	return nobject;
}

IParticleBoxEmitter*createBoxEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section);
IParticleCylinderEmitter*createCylinderEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section);
IParticleRingEmitter*createRingEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section);
IParticleSphereEmitter*createSphereEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section);
IParticleEmitter*createPointEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section);

HObj* ParticleLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	IParticleSystemSceneNode*particleSystem=(IParticleSystemSceneNode*)object->getInternalNode();
	wstring emmiterType=section->readString(section->getChunk(L"emitterType"));
	IParticleEmitter*emitter = NULL;
	if(Utils::equal(emmiterType, L"boxEm")){
		emitter=createBoxEmitter(particleSystem, section);
	}else if(Utils::equal(emmiterType, L"cylinderEm")){
		emitter=createCylinderEmitter(particleSystem, section);
	}else if(Utils::equal(emmiterType, L"ringEm")){
		emitter=createRingEmitter(particleSystem, section);
	}else if(Utils::equal(emmiterType, L"sphereEm")){
		emitter=createSphereEmitter(particleSystem, section);
	}else if(Utils::equal(emmiterType, L"pointEm")){
		emitter=createPointEmitter(particleSystem, section);
	}

	vector3df direction=section->readVector(section->getChunk(L"direction"));
	emitter->setDirection(direction);
	int maxAngleDegrees=section->readInt(section->getChunk(L"maxAngleDegr"));
	emitter->setMaxAngleDegrees(maxAngleDegrees);

	long maxLifeTime=(long)section->readLong(section->getChunk(L"maxLifeTime"));
	emitter->setMaxLifeTime(maxLifeTime);

	long maxPartPerSec=(long)section->readLong(section->getChunk(L"maxPartPerSec"));
	emitter->setMaxParticlesPerSecond(maxPartPerSec);

	video::SColor maxStartColor=section->readSColor(section->getChunk(L"maxStartColor"));
	emitter->setMaxStartColor(maxStartColor);

	dimension2df maxStartSize=section->readDimension2df(section->getChunk(L"maxStartSize"));
	emitter->setMaxStartSize(maxStartSize);

	long minLifeTime=(long)section->readLong(section->getChunk(L"minLifeTime"));
	emitter->setMinLifeTime(minLifeTime);

	long minPartPerSec=(long)section->readLong(section->getChunk(L"minPartPerSec"));
	emitter->setMinParticlesPerSecond(minPartPerSec);

	video::SColor minStartColor=section->readSColor(section->getChunk(L"minStartColor"));
	emitter->setMinStartColor(minStartColor);

	dimension2df minStartSize=section->readDimension2df(section->getChunk(L"minStartSize"));
	emitter->setMinStartSize(minStartSize);

	particleSystem->setEmitter(emitter);
	return object;
}

void moveAABBBox(aabbox3df&box, vector3df&newCenter){
	vector3df tCenter= box.getCenter();
	vector3df delta=newCenter-tCenter;
	box.MaxEdge=box.MaxEdge+delta;
	box.MinEdge=box.MinEdge+delta;
}

void setBoxEmitterSize(IParticleBoxEmitter*emitter, vector3df&sz){
	aabbox3df box=emitter->getBox();
	vector3df tCenter=box.getCenter();
	vector3df min=box.MinEdge;
	vector3df max=box.MaxEdge;
	float x=(float)(sz.X/2.0);
	float y=(float)(sz.Y/2.0);
	float z=(float)(sz.Z/2.0);

	min.X=-x+tCenter.X;
	min.Y=-y+tCenter.Y;
	min.Z=-z+tCenter.Z;

	max.X=x+tCenter.X;
	max.Y=y+tCenter.Y;
	max.Z=z+tCenter.Z;

	box.MaxEdge=max;
	box.MinEdge=min;
	emitter->setBox(box);
}

IParticleBoxEmitter*createBoxEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section){
	IParticleBoxEmitter*boxEmitter=particleSystem->createBoxEmitter();
	FileSection*emittersection=section->readSection(section->getChunk(L"boxEm"));
	vector3df center=emittersection->readVector(emittersection->getChunk(L"center"));
	vector3df size=emittersection->readVector(emittersection->getChunk(L"size"));
	aabbox3df currentBox=boxEmitter->getBox();
	moveAABBBox(currentBox, center);
	boxEmitter->setBox(currentBox);
	setBoxEmitterSize(boxEmitter,size);

	delete emittersection;
	emittersection=NULL;

	return boxEmitter;
}

IParticleCylinderEmitter*createCylinderEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section){
	IParticleCylinderEmitter*emitter=particleSystem->createCylinderEmitter(vector3df(0,0,0),1.0,vector3df(0,1,0),1);
	FileSection*emittersection=section->readSection(section->getChunk(L"cylinderEm"));
	vector3df center=emittersection->readVector(emittersection->getChunk(L"center"));
	float radius=emittersection->readFloat(emittersection->getChunk(L"radius"));
	float length=emittersection->readFloat(emittersection->getChunk(L"length"));
	bool outline=emittersection->readBoolean(emittersection->getChunk(L"outline"));
	emitter->setCenter(center);
	emitter->setRadius(radius);
	emitter->setLength(length);
	emitter->setOutlineOnly(outline);
	delete emittersection;
	emittersection=NULL;
	return emitter;
}

IParticleRingEmitter*createRingEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section){
	IParticleRingEmitter*emitter=particleSystem->createRingEmitter(vector3df(0,0,0),1.0,1);
	FileSection*emittersection=section->readSection(section->getChunk(L"ringEm"));
	vector3df center=emittersection->readVector(emittersection->getChunk(L"center"));
	float radius=emittersection->readFloat(emittersection->getChunk(L"radius"));
	float thick=emittersection->readFloat(emittersection->getChunk(L"thick"));
	
	emitter->setCenter(center);
	emitter->setRadius(radius);
	emitter->setRingThickness(thick);
	delete emittersection;
	emittersection=NULL;
	return emitter;
}

IParticleSphereEmitter*createSphereEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section){
	IParticleSphereEmitter*emitter=particleSystem->createSphereEmitter(vector3df(0,0,0),1.0);
	FileSection*emittersection=section->readSection(section->getChunk(L"sphereEm"));
	vector3df center=emittersection->readVector(emittersection->getChunk(L"center"));
	float radius=emittersection->readFloat(emittersection->getChunk(L"radius"));
	
	emitter->setCenter(center);
	emitter->setRadius(radius);
	delete emittersection;
	emittersection=NULL;
	return emitter;
}

IParticleEmitter*createPointEmitter(IParticleSystemSceneNode*particleSystem, FileSection*section){
	IParticleEmitter*emitter=particleSystem->createPointEmitter();
	return emitter;
}

HObj* ScriptLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	wstring path=section->readString(section->getChunk(L"path"));
	BaseHikariScript*script=context->getScriptManager()->instantiate(path);
	if(script!=NULL){
		ScriptHComponent*scriptComponent=new ScriptHComponent(script);
		Chunk*componentNameChunk=section->getChunk(L"name");
		if(componentNameChunk!=NULL){
			wstring componentName=section->readString(componentNameChunk);
			scriptComponent->setName(componentName);
		}
		Chunk*componentEnabledChunk=section->getChunk(L"enabled");
		if(componentEnabledChunk!=NULL){
			bool componentEnabled=section->readBoolean(componentEnabledChunk);
			scriptComponent->setEnabled(componentEnabled);
		}
		object->addComponent(scriptComponent);
	}
	
	return object;
}

HObj* NavmeshLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	ISceneNode*node=context->getSceneManager()->addEmptySceneNode();
	object=new HObj(node);
	return object;
}

HObj* NavmeshSettingsLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	return object;
}

HObj* PathFindAgentLoadHandler::read(HikariContext*context, HObj*object, FileSection*section){
	PathFindAgentHComponent*agent=new PathFindAgentHComponent(object);
	object->addComponent(agent);
	return object;
}